package com.itheima.w_util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.itheima.w_conf.StringPlusUtils;
import com.itheima.w_conf.SupWarsConstant;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;


/**
 * stream流工具类
 *
 * @author dingwen, 芋道源码
 * @since 2023/07/20
 */
@SuppressWarnings(SupWarsConstant.UNUSED)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class StreamUtils {


    /**
     * 将collection通过map收集为List
     *
     * @param collection collection
     * @param mapper     mapper
     * @return list
     */
    public static <T, R> List<R> map(Collection<T> collection, Function<T, R> mapper) {
        if (collection.isEmpty()) {
            return Collections.emptyList();
        }
        return collection.stream().map(mapper).collect(Collectors.toList());
    }

    /**
     * 将collection过滤
     *
     * @param collection 需要转化的集合
     * @param function   过滤方法
     * @return 过滤后的list
     */
    public static <T> List<T> filter(Collection<T> collection, Predicate<T> function) {
        if (CollUtil.isEmpty(collection)) {
            return CollUtil.newArrayList();
        }
        return collection.stream().filter(function).collect(Collectors.toList());
    }

    /**
     * 将collection拼接
     *
     * @param collection 需要转化的集合
     * @param function   拼接方法
     * @return 拼接后的list
     */
    public static <T> String join(Collection<T> collection, Function<T, String> function) {
        return join(collection, function, StringPlusUtils.COMMA);
    }

    /**
     * 将collection拼接
     *
     * @param collection 需要转化的集合
     * @param function   拼接方法
     * @param delimiter  拼接符
     * @return 拼接后的list
     */
    public static <T> String join(Collection<T> collection, Function<T, String> function, CharSequence delimiter) {
        if (CollUtil.isEmpty(collection)) {
            return StringPlusUtils.EMPTY;
        }
        return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter));
    }

    /**
     * 将collection升序排序
     *
     * @param collection 需要转化的集合
     * @param comparing  排序方法
     * @return 排序后的list
     */
    public static <E> List<E> sortedByAsc(Collection<E> collection, Comparator<E> comparing) {
        if (CollUtil.isEmpty(collection)) {
            return CollUtil.newArrayList();
        }
        return collection.stream().sorted(comparing).collect(Collectors.toList());
    }

    /**
     * 将collection安全的升序排序
     * 规避空指针异常
     *
     * @param collection   需要转化的集合
     * @param keyExtractor key方法
     * @return 排序后的list
     */
    public static <E> List<E> safeSortedByAsc(Collection<E> collection, Function<E, String> keyExtractor) {
        if (CollUtil.isEmpty(collection)) {
            return CollUtil.newArrayList();
        }
        Comparator<E> comparing = Comparator.comparing(keyExtractor, Comparator.nullsFirst(Comparator.naturalOrder()));
        return collection.stream().sorted(comparing).collect(Collectors.toList());
    }

    /**
     * 将collection安全的降序排序
     *
     * @param collection   需要转化的集合
     * @param keyExtractor key方法
     * @return 排序后的list
     */
    public static <E> List<E> safeSortedByDesc(Collection<E> collection, Function<E, String> keyExtractor) {
        if (CollUtil.isEmpty(collection)) {
            return CollUtil.newArrayList();
        }
        Comparator<E> comparing = Comparator.comparing(keyExtractor, Comparator.nullsFirst(Comparator.naturalOrder()));
        return collection.stream().sorted(comparing.reversed()).collect(Collectors.toList());
    }


    /**
     * 将collection降序排序
     *
     * @param collection 需要转化的集合
     * @param comparing  排序方法
     * @return 排序后的list
     */
    public static <E> List<E> sortedByDesc(Collection<E> collection, Comparator<E> comparing) {
        if (CollUtil.isEmpty(collection)) {
            return CollUtil.newArrayList();
        }
        return collection.stream().sorted(comparing.reversed()).collect(Collectors.toList());
    }

    /**
     * 将collection转化为类型不变的map<br>
     * <B>{@code Collection<V>  ---->  Map<K,V>}</B>
     *
     * @param collection 需要转化的集合
     * @param key        V类型转化为K类型的lambda方法
     * @param <V>        collection中的泛型
     * @param <K>        map中的key类型
     * @return 转化后的map
     */
    public static <V, K> Map<K, V> toIdentityMap(Collection<V> collection, Function<V, K> key) {
        if (CollUtil.isEmpty(collection)) {
            return MapUtil.newHashMap();
        }
        return collection.stream().collect(Collectors.toMap(key, Function.identity(), (l, r) -> l));
    }

    /**
     * 将Collection转化为map(value类型与collection的泛型不同)<br>
     * <B>{@code Collection<E> -----> Map<K,V>  }</B>
     *
     * @param collection 需要转化的集合
     * @param key        E类型转化为K类型的lambda方法
     * @param value      E类型转化为V类型的lambda方法
     * @param <T>        collection中的泛型
     * @param <K>        map中的key类型
     * @param <V>        map中的value类型
     * @return 转化后的map
     */
    public static <T, K, V> Map<K, V> toMap(Collection<T> collection, Function<T, K> key, Function<T, V> value) {
        if (CollUtil.isEmpty(collection)) {
            return MapUtil.newHashMap();
        }
        return collection.stream().collect(Collectors.toMap(key, value, (l, r) -> l));
    }

    /**
     * 将collection按照规则(比如有相同的班级id)分类成map<br>
     * <B>{@code Collection<E> -------> Map<K,List<E>> } </B>
     *
     * @param collection 需要分类的集合
     * @param key        分类的规则
     * @param <T>        collection中的泛型
     * @param <K>        map中的key类型
     * @return 分类后的map
     */
    public static <T, K> Map<K, List<T>> groupByKey(Collection<T> collection, Function<T, K> key) {
        if (CollUtil.isEmpty(collection)) {
            return MapUtil.newHashMap();
        }
        return collection.stream().collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList()));
    }

    /**
     * group2 map
     * 任务分组
     *
     * @param collection collection
     * @param groupSize  group size
     * @return map
     */
    public static <E> Map<Integer, List<E>> group2Map(Collection<E> collection, Integer groupSize) {
        if (CollUtil.isEmpty(collection)) {
            return Collections.emptyMap();
        }
        if (Objects.isNull(groupSize)) {
            groupSize = collection.size();
        }
        int finalGroupSize = groupSize;
        return IntStream.range(0, (collection.size() + groupSize - 1) / groupSize)
                        .boxed()
                        .collect(Collectors.toMap(
                                i -> i,
                                i -> collection.stream().skip((long) i * finalGroupSize).limit(finalGroupSize).collect(Collectors.toList())));
    }

    /**
     * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map<br>
     * <B>{@code Collection<E>  --->  Map<T,Map<U,List<E>>> } </B>
     *
     * @param collection 需要分类的集合
     * @param key1       第一个分类的规则
     * @param key2       第二个分类的规则
     * @param <T>        集合元素类型
     * @param <K>        第一个map中的key类型
     * @param <U>        第二个map中的key类型
     * @return 分类后的map
     */
    public static <T, K, U> Map<K, Map<U, List<T>>> groupBy2Key(Collection<T> collection, Function<T, K> key1,
                                                                Function<T, U> key2) {
        if (CollUtil.isEmpty(collection)) {
            return MapUtil.newHashMap();
        }
        return collection.stream().collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2
                , LinkedHashMap::new, Collectors.toList())));
    }

    /**
     * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map<br>
     * <B>{@code Collection<E>  --->  Map<T,Map<U,E>> } </B>
     *
     * @param collection 需要分类的集合
     * @param key1       第一个分类的规则
     * @param key2       第二个分类的规则
     * @param <K>        第一个map中的key类型
     * @param <U>        第二个map中的key类型
     * @param <T>        collection中的泛型
     * @return 分类后的map
     */
    public static <T, K, U> Map<K, Map<U, T>> group2Map(Collection<T> collection, Function<T, K> key1,
                                                        Function<T, U> key2) {
        if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) {
            return MapUtil.newHashMap();
        }
        return collection.stream().collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2,
                Function.identity(), (l, r) -> l)));
    }

    /**
     * 将collection转化为List集合，但是两者的泛型不同<br>
     * <B>{@code Collection<E>  ------>  List<T> } </B>
     *
     * @param collection 需要转化的集合
     * @param function   collection中的泛型转化为list泛型的lambda表达式
     * @param <T>        collection中的泛型
     * @param <R>        List中的泛型
     * @return 转化后的list
     */
    public static <T, R> List<R> toList(Collection<T> collection, Function<T, R> function) {
        if (CollUtil.isEmpty(collection)) {
            return CollUtil.newArrayList();
        }
        return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 将collection转化为Set集合，但是两者的泛型不同<br>
     * <B>{@code Collection<E>  ------>  Set<T> } </B>
     *
     * @param collection 需要转化的集合
     * @param function   collection中的泛型转化为set泛型的lambda表达式
     * @param <T>        collection中的泛型
     * @param <R>        Set中的泛型
     * @return 转化后的Set
     */
    public static <T, R> Set<R> toSet(Collection<T> collection, Function<T, R> function) {
        if (CollUtil.isEmpty(collection) || function == null) {
            return CollUtil.newHashSet();
        }
        return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.toSet());
    }


    /**
     * 合并两个相同key类型的map
     *
     * @param map1  第一个需要合并的 map
     * @param map2  第二个需要合并的 map
     * @param merge 合并的lambda，将key  value1 value2合并成最终的类型,注意value可能为空的情况
     * @param <K>   map中的key类型
     * @param <X>   第一个 map的value类型
     * @param <Y>   第二个 map的value类型
     * @param <V>   最终map的value类型
     * @return 合并后的map
     */
    public static <K, X, Y, V> Map<K, V> merge(Map<K, X> map1, Map<K, Y> map2, BiFunction<X, Y, V> merge) {
        if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) {
            return MapUtil.newHashMap();
        } else if (MapUtil.isEmpty(map1)) {
            map1 = MapUtil.newHashMap();
        } else if (MapUtil.isEmpty(map2)) {
            map2 = MapUtil.newHashMap();
        }
        Set<K> key = new HashSet<>();
        key.addAll(map1.keySet());
        key.addAll(map2.keySet());
        Map<K, V> map = new HashMap<>(16);
        for (K t : key) {
            X x = map1.get(t);
            Y y = map2.get(t);
            V z = merge.apply(x, y);
            if (z != null) {
                map.put(t, z);
            }
        }
        return map;
    }

    /**
     * 求和
     *
     * @param collection 集合
     * @param mapper     求和字段
     * @param predicate  过滤空值
     * @return {@link Integer}
     */
    public static <T> Integer sum(Collection<T> collection, ToIntFunction<? super T> mapper,
                                  Predicate<? super T> predicate) {
        return collection.stream().filter(predicate).mapToInt(mapper).sum();
    }


    /**
     * 总和
     *
     * @param collection 集合
     * @param predicate  谓词
     * @return {@link BigDecimal}
     */
    public static BigDecimal sum(Collection<BigDecimal> collection, Predicate<? super BigDecimal> predicate) {
        return collection.stream().filter(predicate).reduce(BigDecimal.ZERO, BigDecimal::add);
    }


    /**
     * 收集单字段
     *
     * @param collection 集合
     * @param mapper     映射器
     * @return {@link Collection}
     */
    public static <T, R> Collection<R> collect(Collection<T> collection, Function<T, R> mapper) {
        return collection.stream().map(mapper).collect(Collectors.toList());
    }


    /**
     * List 去重
     *
     * @param data          数据
     * @param comparingFunc 比较函数
     * @return list
     */
    public static <T, U extends Comparable<? super U>> List<T> distinct(List<T> data, Function<? super T,
            ? extends U> comparingFunc) {
        return data.stream()
                   .collect(
                           Collectors.collectingAndThen(
                                   Collectors.toCollection(
                                           () -> new TreeSet<>(Comparator.comparing(comparingFunc))
                                   ),
                                   ArrayList::new
                           )
                   );
    }

    /**
     * List 去重,基本数据类型
     *
     * @param data 数据
     * @return 结果
     */
    public static <T> List<T> distinct(List<T> data) {
        return data.stream().distinct().collect(Collectors.toList());
    }


    /**
     * BigDecimal 安全求和
     *
     * @param data data
     * @return {@link BigDecimal }
     */
    public static BigDecimal safeAdds(List<BigDecimal> data) {
        if (CollUtil.isEmpty(data)) {
            return BigDecimal.ZERO;
        }
        return data.stream()
                   .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    /**
     * BigDecimal 安全求和
     *
     * @param data   data
     * @param mapper mapper
     * @return {@link BigDecimal }
     */
    public static <T> BigDecimal safeAdds(List<T> data, Function<T, BigDecimal> mapper) {
        return safeAdds(map(data, mapper));
    }

}
